package controller;

import model.Aluno;
import model.Disciplina;

import java.sql.ResultSet;
import java.util.ArrayList;
import model.Notas;

/**
 * Classe que representa um Data Access Object para acessar/modificar a tabela NOTAS
 * do banco de dados do SADAF
 * Esta classe segue o padrão SINGLETON.
 * OBS: CHAVE PRIMÁRIA DA TABELA: MATRICULA DISCPLINA SEMESTRE
 * @author Adriano
 * @see http://en.wikipedia.org/wiki/Singleton_pattern
 */
public class NotasDAO {

    // Instância única do NotasDAO.
    private static final NotasDAO INSTANCE = new NotasDAO();
    // Instância única da lista de Notas
    private ArrayList<Notas> listaDeNotas;

    // Construtor privado para que outras classes não instanciem o NotasDAO
    private NotasDAO() {
        this.listaDeNotas = this.montarListaDeNotas();
    }

    /**
     * O acesso ao objeto NotasDAO deve ser feito através do método getInstance.
     * Isto garante apenas uma instância de NotasDAO.
     */
    public static NotasDAO getInstance() {
        return INSTANCE;
    }

    /**
     * Retorna a lista de notas
     * @return
     */
    public ArrayList<Notas> getListaDeNotas() {
        return this.listaDeNotas;
    }

    /**
     * Efetua um acesso na tabela de  NOTAS e retorna uma lista de Notas
     * cadastrados no banco de dados do SADAF.
     * @return ArrayList<Notas> contendo todas as Notas do bd.
     */
    private ArrayList<Notas> montarListaDeNotas() {

        ArrayList<Notas> retArray = new ArrayList<Notas>();
        BancoDeDados bd = new BancoDeDados();

        try {
            bd.carregarDriver();
            bd.conectar(bd.bdUrl, bd.bdUsuario, bd.bdSenha);
            bd.sql = "SELECT * FROM NOTAS";
            bd.comandoSql = bd.connection.prepareStatement(bd.sql);
            ResultSet rs = bd.comandoSql.executeQuery();

            while (rs.next()) {
                String matriculaAluno = rs.getString(1);
                String codigoDisciplina = rs.getString(2);
                ArrayList<Double> notasDoAluno = new ArrayList<Double>();

                if (rs.getDouble(3) != -1) {
                    notasDoAluno.add(rs.getDouble(3));    // NOTA_1
                }
                if (rs.getDouble(4) != -1) {
                    notasDoAluno.add(rs.getDouble(4));    // NOTA_2
                }
                if (rs.getDouble(5) != -1) {
                    notasDoAluno.add(rs.getDouble(5));    // NOTA_3
                }
                if (rs.getDouble(6) != -1) {
                    notasDoAluno.add(rs.getDouble(6));    // NOTA_FINAL
                }
                double media = rs.getDouble(7); // MEDIA
                String status = rs.getString(8);
                String semestre = rs.getString(9);
                Aluno aluno = (AlunoDAO.getInstance().ProcurarAluno(matriculaAluno));
                Disciplina disciplina = (DisciplinaDAO.getInstance().ProcuraDisciplina(codigoDisciplina));
                Notas nota = new Notas(aluno, disciplina, notasDoAluno, media, status, semestre);
                retArray.add(nota);
            }
            bd.desconectar();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retArray;
    }

    /**
     * Monta lista das DISCIPLINAS que o aluno está matriculado.
     * //TODO: Testar modificação para retornar disciplinas apenas EM CURSO
     * @param matricula Matricula do aluno que será examinado.
     */
    public ArrayList<Disciplina> ArrayListDisciplinaAluno(String matricula) {
        ArrayList<Disciplina> disciplinaArray = new ArrayList<Disciplina>();

        int tamanhoDaListaDeNotas = this.listaDeNotas.size();

        for (int i = 0; i < tamanhoDaListaDeNotas; i++) {
            if ((matricula.equals(this.listaDeNotas.get(i).getAluno().getMatricula()) == true) && (this.listaDeNotas.get(i).getStatus().equals("EM CURSO"))) {
                disciplinaArray.add(this.listaDeNotas.get(i).getDisciplina());
            }
        }

        return disciplinaArray;
    }

    /**
     * Efetua a matrícula de um Aluno em uma Disciplina.
     * @param notas Objeto Notas contendo a Disciplina e o Aluno a ser matriculado.
     */
    public void matricularAluno(Notas notas) {
        BancoDeDados bd = new BancoDeDados();
        try {
            bd.carregarDriver();
            bd.conectar(bd.bdUrl, bd.bdUsuario, bd.bdSenha);
            bd.sql = "INSERT INTO NOTAS "
                    + "(MATRICULA_ALUNO,CODIGO_DISCIPLINA, NOTA_1,NOTA_2,NOTA_3,NOTA_FINAL,MEDIA,STATUS,SEMESTRE )"
                    + "VALUES (?,?,?,?,?,?,?,?,?)";
            bd.comandoSql = bd.connection.prepareStatement(bd.sql);
            bd.comandoSql.setString(1, notas.getAluno().getMatricula());
            bd.comandoSql.setString(2, notas.getDisciplina().getCodigo());
            bd.comandoSql.setDouble(3, -1.0);
            bd.comandoSql.setDouble(4, -1.0);
            bd.comandoSql.setDouble(5, -1.0);
            bd.comandoSql.setDouble(6, -1.0);
            bd.comandoSql.setDouble(7, -1.0);
            bd.comandoSql.setString(8, notas.getStatus());
            bd.comandoSql.setString(9, notas.getSemestre());
            bd.comandoSql.executeUpdate();
            bd.desconectar();

            this.listaDeNotas.add(notas);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Monta uma lista de notas do Aluno passado como parâmetro.
     * @param aluno
     * @param tabela
     */
    public ArrayList<Notas> montaListNotasDoAluno(Aluno aluno) {
        ArrayList<Notas> arrayList = new ArrayList<Notas>();
        int j=listaDeNotas.size();
        int i=0;

        while(i<j){
            if(listaDeNotas.get(i).getAluno().getMatricula().equals(aluno.getMatricula()))
                arrayList.add(listaDeNotas.get(i));
            i++;
        }
        
        return arrayList;
    }


    /**
     * Exclui todas as linhas de um dado aluno da tabela de Notas. Deve
     * ser usado ao excluir um aluno do banco de dados.
     * @param aluno Aluno a ser excluído.
     */
    public void excluirAlunoDaTabelaDeNotas(Aluno aluno) {

        BancoDeDados bd = new BancoDeDados();

        try {
            bd.carregarDriver();
            bd.conectar(bd.bdUrl, bd.bdUsuario, bd.bdSenha);
            bd.sql = "DELETE FROM NOTAS WHERE MATRICULA_ALUNO = ?";
            bd.comandoSql = bd.connection.prepareStatement(bd.sql);
            bd.comandoSql.setString(1, aluno.getMatricula());
            bd.comandoSql.executeUpdate();
            bd.desconectar();

            for (int i = 0; i < this.listaDeNotas.size(); i++) {
                if (listaDeNotas.get(i).getAluno().getMatricula().equals(aluno.getMatricula())) {
                    listaDeNotas.remove(listaDeNotas.get(i));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Exclui todas as linhas de uma dada disciplina da tabela de Notas. Deve
     * ser usado ao excluir um disciplina do banco de dados.
     * @param disciplina Disciplina a ser excluída.
     */
    public void excluirDisciplinaDaTabelaDeNotas(Disciplina disciplina) {

        BancoDeDados bd = new BancoDeDados();
        try {
            bd.carregarDriver();
            bd.conectar(bd.bdUrl, bd.bdUsuario, bd.bdSenha);
            bd.sql = "DELETE FROM NOTAS WHERE CODIGO_DISCIPLINA = ?";
            bd.comandoSql = bd.connection.prepareStatement(bd.sql);
            bd.comandoSql.setString(1, disciplina.getCodigo());
            bd.comandoSql.executeUpdate();
            bd.desconectar();

            for (int i = 0; i < this.listaDeNotas.size(); i++) {
                if (listaDeNotas.get(i).getDisciplina().getCodigo().equals(disciplina.getCodigo())) {
                    listaDeNotas.remove(listaDeNotas.get(i));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Exclui a linha que corresponde ao aluno e a disciplina passados como
     * parametro. Na prática, cancela a matrícula do aluno.
     * TODO: refazer esse método. Passar objeto Notas como parâmetro e excluir
     * a linha usando matricula_aluno, codigo_disciplina e semestre.
     * @param disciplina Disciplina a ser excluída.
     * @param aluno a ser excluído.
     */
    public void excluirAlunoDeDisciplina(Aluno aluno, Disciplina disciplina) {

        BancoDeDados bd = new BancoDeDados();
        try {
            bd.carregarDriver();
            bd.conectar(bd.bdUrl, bd.bdUsuario, bd.bdSenha);
            bd.sql = "DELETE FROM NOTAS WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ?";
            bd.comandoSql = bd.connection.prepareStatement(bd.sql);
            bd.comandoSql.setString(1, aluno.getMatricula());
            bd.comandoSql.setString(2, disciplina.getCodigo());
            bd.comandoSql.executeUpdate();
            bd.desconectar();

            //atualiza a lista de notas.
            for (int i = 0; i < this.listaDeNotas.size(); i++) {
                if ((listaDeNotas.get(i).getDisciplina().getCodigo().equals(disciplina.getCodigo())) && (listaDeNotas.get(i).getAluno().getMatricula().equals(aluno.getMatricula()))){
                  listaDeNotas.remove(listaDeNotas.get(i));
                  break;
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Procura um objeto Notas na lista de Notas.
     * @param matricula Matricula do aluno procurado.
     * @param codigo Disciplinao onde o aluno está matriculado.
     * @return Objeto Notas representando a matrícula do aluno
     */
    public Notas ProcuraNota(String matricula, String codigo, String semestre) {

        Notas retNota = null;
        int tamanhoDaListaDeNotas = listaDeNotas.size();

        for (int j = 0; j < tamanhoDaListaDeNotas; j++) {
            if (listaDeNotas.get(j).getDisciplina().getCodigo().equals(codigo)
                && listaDeNotas.get(j).getAluno().getMatricula().equals(matricula)
                && listaDeNotas.get(j).getSemestre().equals(semestre)){
                retNota = listaDeNotas.get(j);
                break;
            }
        }

        return retNota;
    }

    /**
     * Atualiza as notas do objeto no banco de dados.
     * @param nota
     * @param notaLista
     * @param valor
     */
    public void setNotas(String nota, Notas notaLista, double valor) {

        BancoDeDados bd = new BancoDeDados();
        boolean canExecute = true;

        try {
            bd.carregarDriver();
            bd.conectar(bd.bdUrl, bd.bdUsuario, bd.bdSenha);

            // Procura o aluno na listaDeNotas
            int tamanhoDaListaDeNotas = listaDeNotas.size();
            for (int i = 0; i < tamanhoDaListaDeNotas; i++) {
                if (((notaLista.getAluno().getMatricula().equals(listaDeNotas.get(i).getAluno().getMatricula()))) && (notaLista.getDisciplina().getCodigo().equals(listaDeNotas.get(i).getDisciplina().getCodigo()))) {

                    // aluno encontrado, checa o tamanho do array de notas
                    switch (listaDeNotas.get(i).getNotas().size()) {
                        case 0:
                            if (nota.equals("NOTA_1")) {
                                bd.sql = "UPDATE NOTAS SET NOTA_1 = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().add(valor);
                            } else {
                                // TODO: implementar exceção dizendo que tem que lançar a nota 1
                                canExecute = false;
                            }
                            break;
                        case 1:
                            if (nota.equals("NOTA_1")) {
                                bd.sql = "UPDATE NOTAS SET NOTA_1 = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().set(0, valor);
                            } else if (nota.equals("NOTA_2")) {
                                bd.sql = "UPDATE NOTAS SET NOTA_2 = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().add(valor);
                            } else {
                                // TODO: implementar exceção dizendo que tem que lançar a nota 1 ou 2
                                canExecute = false;
                            }
                            break;
                        case 2:
                            if (nota.equals("NOTA_1")) {
                                bd.sql = "UPDATE NOTAS SET NOTA_1 = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().set(0, valor);
                            } else if (nota.equals("NOTA_2")) {
                                bd.sql = "UPDATE NOTAS SET NOTA_2 = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().set(1, valor);
                            } else if (nota.equals("NOTA_3")) {
                                bd.sql = "UPDATE NOTAS SET NOTA_3 = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().add(valor);
                            } else {
                                // TODO: implementar exceção dizendo que tem que lançar a nota 1, 2 ou 3
                                canExecute = false;
                            }
                            break;
                        // size == 3
                        default:
                            if (nota.equals("NOTA_1")) {
                                bd.sql = "UPDATE NOTAS SET NOTA_1 = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().set(0, valor);
                            } else if (nota.equals("NOTA_2")) {
                                bd.sql = "UPDATE NOTAS SET NOTA_2 = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().set(1, valor);
                            } else if (nota.equals("NOTA_3")) {
                                bd.sql = "UPDATE NOTAS SET NOTA_3 = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().set(2, valor);
                            } else { // nota final
                                // TODO: implementar exceção dizendo que tem que lançar a nota 1, 2 ou 3
                                bd.sql = "UPDATE NOTAS SET NOTA_FINAL = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
                                listaDeNotas.get(i).getNotas().add(valor);
                            }
                            break;
                    }

                    if (canExecute) {
                        bd.comandoSql = bd.connection.prepareStatement(bd.sql);
                        bd.comandoSql.setDouble(1, valor);
                        bd.comandoSql.setString(2, notaLista.getAluno().getMatricula());
                        bd.comandoSql.setString(3, notaLista.getDisciplina().getCodigo());
                        bd.comandoSql.setString(4, notaLista.getSemestre());
                        bd.comandoSql.executeUpdate();
                        bd.desconectar();
                    }

                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Atualia a média do objeto Notas no banco.
     * @param nota
     */
    public void atualizaMedia(Notas nota) {

        BancoDeDados bd = new BancoDeDados();
        try {
            bd.carregarDriver();
            bd.conectar(bd.bdUrl, bd.bdUsuario, bd.bdSenha);
            bd.sql = "UPDATE NOTAS SET MEDIA = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ?";
            bd.comandoSql = bd.connection.prepareStatement(bd.sql);
            bd.comandoSql.setDouble(1, nota.getMedia());
            bd.comandoSql.setString(2, nota.getAluno().getMatricula());
            bd.comandoSql.setString(3, nota.getDisciplina().getCodigo());
            bd.comandoSql.setString(4, nota.getSemestre());
            bd.comandoSql.executeUpdate();
            bd.desconectar();

            int j = listaDeNotas.size();
            for (int i = 0; i < j; i++) {
                if (listaDeNotas.get(i).getDisciplina().getCodigo().equals(nota.getDisciplina().getCodigo()) && listaDeNotas.get(i).getAluno().getMatricula().equals(nota.getAluno().getMatricula())) {
                    listaDeNotas.get(i).setMedia(nota.getMedia());
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Atualia a média do objeto Notas no banco.
     * @param nota
     */
    public void atualizaStatus(Notas nota) {

        BancoDeDados bd = new BancoDeDados();
        try {
            bd.carregarDriver();
            bd.conectar(bd.bdUrl, bd.bdUsuario, bd.bdSenha);
            bd.sql = "UPDATE NOTAS SET STATUS = ? WHERE MATRICULA_ALUNO = ? AND CODIGO_DISCIPLINA = ? AND SEMESTRE = ? ";
            bd.comandoSql = bd.connection.prepareStatement(bd.sql);
            bd.comandoSql.setString(1, nota.getStatus());
            bd.comandoSql.setString(2, nota.getAluno().getMatricula());
            bd.comandoSql.setString(3, nota.getDisciplina().getCodigo());
            bd.comandoSql.setString(4, nota.getSemestre());
            bd.comandoSql.executeUpdate();
            bd.desconectar();

            int j = listaDeNotas.size();
            for (int i = 0; i < j; i++) {
                if (listaDeNotas.get(i).getDisciplina().getCodigo().equals(nota.getDisciplina().getCodigo()) && listaDeNotas.get(i).getAluno().getMatricula().equals(nota.getAluno().getMatricula())) {
                    listaDeNotas.get(i).setStatus(nota.getStatus());
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

public ArrayList<Aluno> montaListAlunosDeDisciplina(Disciplina disciplina){
        ArrayList<Aluno> arrayList=new ArrayList<Aluno>();
        int i=listaDeNotas.size();
        int j=0;

        while(j<i){
            if(listaDeNotas.get(j).getDisciplina().getCodigo().equals(disciplina.getCodigo()))
                arrayList.add(listaDeNotas.get(j).getAluno());
            j++;
        }
        return arrayList;
    }

public ArrayList<Aluno> montaListAlunosDeDisciplinaSemestre(Disciplina disciplina,String semestre){
        ArrayList<Aluno> arrayList=new ArrayList<Aluno>();
        int i=listaDeNotas.size();
        int j=0;

        while(j<i){
            if(listaDeNotas.get(j).getDisciplina().getCodigo().equals(disciplina.getCodigo())&&listaDeNotas.get(j).getSemestre().equals(semestre))
                arrayList.add(listaDeNotas.get(j).getAluno());
            j++;
        }
        return arrayList;
    }
}
